package utils

/**
  主要用于异步请求响应session转同步

*/

import (
	"fmt"
	"runtime"
	"sync"
	"sync/atomic"
	"time"
)

type RequestSession struct {
	uuid     string
	doneflag int8
	waitchan chan byte
	lk       sync.RWMutex
	start    time.Time
	resp     interface{}
}

var (
	request_session_obj_cnt int32
	request_session_pool    sync.Pool
	request_session_lk      sync.RWMutex
	request_session_waitMap map[string]*RequestSession
)

func init() {
	request_session_waitMap = make(map[string]*RequestSession)
	request_session_pool.New = func() interface{} {
		rval := &RequestSession{}
		atomic.AddInt32(&request_session_obj_cnt, 1)
		runtime.SetFinalizer(rval, func(obj interface{}) {
			atomic.AddInt32(&request_session_obj_cnt, -1)
		})
		return rval
	}
}

func putback(sess *RequestSession) {
	request_session_lk.Lock()
	defer request_session_lk.Unlock()
	delete(request_session_waitMap, sess.uuid)
	if sess.doneflag == 0 {
		panic("doneflag is 0")
	}
	sess.doneflag = 0
	sess.uuid = ""
	sess.resp = nil
	sess.waitchan = nil
	request_session_pool.Put(sess)
}

func GetRequestSessionStatus() string {
	request_session_lk.RLock()
	defer request_session_lk.RUnlock()
	return fmt.Sprintf("obj:%d, wait:%d", request_session_obj_cnt, len(request_session_waitMap))
}

/*
*

	必须执行GetRequestSessionWaitResp
	uuid :必须全局唯一
*/
func GetRequestSession(uuid string) *RequestSession {
	request_session_lk.Lock()
	defer request_session_lk.Unlock()
	sess := request_session_waitMap[uuid]
	if sess != nil {
		return nil
	}

	sess = request_session_pool.Get().(*RequestSession)
	sess.uuid = uuid
	sess.waitchan = make(chan byte, 1)

	// 加入等待列表
	request_session_waitMap[uuid] = sess
	return sess
}

func RequestSessionWaitResp(sess *RequestSession, waittimeout time.Duration) (resp interface{}) {
	if sess == nil {
		return fmt.Errorf("获取的SESSION为空，UUID必须唯一")
	}
	defer putback(sess)
	if sess.wait(waittimeout) {
		return sess.resp
	} else {
		return fmt.Errorf("等待响应超时")
	}
}

func RequestSessionWaitRespEx(sess *RequestSession, waittimeout time.Duration) (ok bool, resp interface{}) {
	if sess == nil {
		return false, fmt.Errorf("获取的SESSION为空，UUID必须唯一")
	}
	defer putback(sess)
	if sess.wait(waittimeout) {
		return true, sess.resp
	} else {
		return false, nil
	}
}

func DoRequestSessionResp(uuid string, resp interface{}) (ok bool) {
	request_session_lk.RLock()
	defer request_session_lk.RUnlock()

	sess := request_session_waitMap[uuid]
	if sess == nil {
		return false
	}

	if sess.uuid != uuid {
		panic(fmt.Sprintf("uuid is not match, [%s!=%s]", uuid, sess.uuid))
	}

	return sess.doresp(resp)
}

func (this *RequestSession) doresp(resp interface{}) (ok bool) {
	this.lk.Lock()
	defer this.lk.Unlock()
	if this.doneflag == 0 {
		this.doneflag = 1
		this.resp = resp
		//flag := this.uuid
		//time.AfterFunc(time.Second*5, func() {
		//	if len(flag) > 0 {
		//		panic("wait ....")
		//	}
		//})
		this.waitchan <- 1
		//flag = ""
		ok = true
	}

	return
}

func (this *RequestSession) wait(timeout time.Duration) bool {
	timer := time.NewTimer(timeout)
	select {
	case <-this.waitchan:
		if !timer.Stop() {
			<-timer.C
		}
		return true
	case <-timer.C:
		ok := false
		this.lk.Lock()
		if this.doneflag == 1 {
			ok = true
		} else {
			this.doneflag = 2
		}
		this.lk.Unlock()
		return ok
	}
}
